WebWork 2 : Chaining Interceptor
This page last changed on Jun 18, 2004 by plightbo.
How to use the Chaining InterceptorThe following code snippet shows how interceptor stacks work for chaining. If someone wants to post xwork.xml (and more complex) examples it would be appreciatedInterceptors-stack A before Action A Interceptor-stack B before Action B Action B result Interceptor-stack B after Interceptor-stack A after Interceptors that wrap Chained ActionsSometimes you may want to have an interceptor that wraps a number of chained actions (and is included in the Interceptor stack for each), but is only invoked at the start and end of the chain. For example, an Interceptor that manages a Hibernate Session / Transaction. Here is an example from my 'teach-myself-webwork-and-hibernate project named 'cash' after Johnny Cash. <interceptor name="hibernate" class="cash.interceptor.HibernateInterceptor"/> <interceptor name="login" class="cash.interceptor.LoginInterceptor"/> <interceptor-stack name="cashDefaultStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="component"/> <interceptor-ref name="hibernate"/> <interceptor-ref name="login"/> </interceptor-stack> <interceptor-stack name="cashValidationWorkflowStack"> <interceptor-ref name="cashDefaultStack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="cashDefaultStack"/> <action name="list" class="cash.action.SelectUserAction"> <result name="success" type="dispatcher">list.vm</result> </action> <action name="edit" class="cash.action.EditAction"> <result name="success" type="chain">list</result> <result name="input" type="dispatcher">edit.vm</result> <interceptor-ref name="cashValidationWorkflowStack"/> </action> In this example, after editing a user, the EditAction is chained to the ListAction to display a list of all users to the screen. We want the following EditActionInterceptorStack - before EditAction ListActionInterceptorStack (except for Hibernate) - before ListAction ListActionInterceptorStack (except for Hibernate) - end EditActinInterceptorStack - end EditActionInterceptorStack - before EditAction ListActionInterceptorStack (including Hibernate) - before ListAction ListActionInterceptorStack (including Hibernate) - end EditActinInterceptorStack - end ERROR! Hibernate Session / Transaction is already closed!!! The way to get the desired behaviour is to either not use a chained action (and incorporate the ListAction logic into EditAction, or to make the HibernateInterceptor smart enough to handle chained actions. The HibernateInterceptor can use a ThreadLocal to hold state and detect if it is inside a chain. When it detects this, it can do nothing. package cash.interceptor; import net.sf.hibernate.HibernateException; import net.sf.hibernate.Transaction; import org.apache.log4j.Logger; import com.opensymphony.xwork.Action; import com.opensymphony.xwork.ActionInvocation; import com.opensymphony.xwork.interceptor.Interceptor; import cash.action.HibernateAction; import cash.util.HibernateUtil; /** * @author Gavin King * @author Joel Hockey * @version $Id: Chaining\040Interceptor.html,v 1.10 2004/12/14 22:46:17 plightbo Exp $ */ public class HibernateInterceptor implements Interceptor { private static final Logger LOG = Logger.getLogger(HibernateInterceptor.class); private static ThreadLocal s_threadLocal = new ThreadLocal(); /** destroy */ public void destroy() { } /** init */ public void init() { } /** implement intercept */ public String intercept(ActionInvocation invocation) throws Exception { LOG.debug("HibernateInterceptor called"); Action action = invocation.getAction(); if (!(action instanceof HibernateAction)) { return invocation.invoke(); } // continue with HibernateAction HibernateAction ha = (HibernateAction)action; // if this interceptor is being chained, then transaction will already exist // in that case, we should let the outer interceptor dispose of the sesion boolean inChainedAction = true; Transaction transaction = (Transaction)s_threadLocal.get(); if (transaction == null) { inChainedAction = false; transaction = HibernateUtil.currentSession().beginTransaction(); s_threadLocal.set(transaction); } boolean rollback = false; try { return invocation.invoke(); } catch (Exception e) { // Note that all the cleanup is done // after the view is rendered, so we // have an open session in the view rollback = true; if (e instanceof HibernateException) { LOG.error("HibernateException in execute()", e); return HibernateAction.DBERROR; } else { LOG.error("Exception in execute()", e); throw e; } } finally { try { if (!inChainedAction) { s_threadLocal.set(null); disposeSession(transaction, ha.getRollback() || rollback); } } catch (HibernateException e) { LOG.error("HibernateException in dispose()", e); return HibernateAction.DBERROR; } } } /** dispose of session */ public void disposeSession(Transaction transaction, boolean rollback) throws HibernateException { LOG.debug("disposing"); if (!HibernateUtil.currentSession().isConnected()) { LOG.debug("Session has already been disposed of - this will happen in a chained action"); return; } try { if (transaction != null) { if (rollback) { LOG.debug("rolling back"); transaction.rollback(); } else { LOG.debug("committing"); transaction.commit(); } } } catch (HibernateException e) { LOG.error("error during commit/rollback", e); if (!rollback && transaction != null) { LOG.error("rolling back affter previous attempt to commit"); transaction.rollback(); } throw e; } finally { HibernateUtil.closeSession(); } } } For more information, also see OS:Webwork - Why would I use Action Chaining? |
Document generated by Confluence on Dec 14, 2004 16:36 |